//Mark O Meara
//08332240

import tcdIO.Terminal;
public class Receiver implements Runnable {


	Channel in= null;							// channel for incoming information/frames
	Channel out= null;							// channel for outgoing information/frames
	byte buffer[]= null;						// buffer for the message that is to be receiver
	int lastreceived = -1;							//Used to keep track of last frame number that was received
	int nextexpected = 0;
	boolean receivecomplete= false;
	Terminal terminal1 = new Terminal("Receiver");


	
	/*
	 * Constructor that accepts incoming and outgoing channels.
	 */
	Receiver(Channel in, Channel out) {
		this(in, out, 10000);
	}
	
	
	/*
	 * Constructor that accepts incoming and outgoing channels
	 * and the length of the buffer that should be reserved.
	 */
	Receiver(Channel in, Channel out, int length) {
		this.in= in;//Channel1
		this.out= out;//Channel2
		buffer= new byte[length];
	}

	
	/*
	 * Thread that attempts to send the message through the 
	 * outgoing channel.
	 */
	public class sendingThread implements Runnable {
		public void run() {
			terminal1.println("In run");
			byte[] ack = new byte[1];//Create new acknowledgement messages
			while(!receivecomplete){
				//If we have received the expected frame
				if(lastreceived == nextexpected){
					//System.out.println("Last received == next expected: " + lastreceived + " " + nextexpected);
					nextexpected = nextexpected + 1;
					ack[0] = (byte)nextexpected;
				}
				else{
					terminal1.println("***NOT EQUAL. Next Expected: " + nextexpected + " Last Received: " + lastreceived);
				}
				terminal1.println("ACK Next expected: " + nextexpected + ". Last Received: " + lastreceived);
				out.send(ack);//Send acknowledgement message

				//Always send acknowledgment, in case last acknowledgement frame was lost
				//terminal1.println("AKC Next expected: " + nextexpected + ". Last Received: " + lastreceived);
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					System.out.println("Interrupted Exception");
					e.printStackTrace();
				}
			}
			/*When complete, send acknowledgement of last frame*/
			nextexpected = lastreceived + 1;
			terminal1.println("ACK Next expected: " + nextexpected + ". Last Received: " + lastreceived);
			ack[0] = (byte)nextexpected;
			out.send(ack);
		}
	}

	/*
	 * Thread that attempts to receive information through the 
	 * incoming channel.
	 */
	public class receivingThread implements Runnable {
		public void run() {
			byte[] packet= null;//empty packet (number + frame)
			int bufferpos = 0;
			
			while(!receivecomplete) {
				packet = in.receive();
				if (packet!=null) {
					System.out.println("Received frame number " + (int)packet[0] + " Last Received: "+lastreceived + " Next Expected: "+nextexpected);
					
					if((int)packet[0] == lastreceived+1){//If frame received is next in sequence
						/*
						 * Send confirmation to sender
						 */
						
						System.out.println("Receiver: " + bufferpos + ":" + (packet.length - 1)); // debug info
						System.arraycopy(packet, 1, buffer, bufferpos, packet.length - 1);//(src,srcPos,dest,destPos,length)
						bufferpos = bufferpos + (packet.length - 1);
						lastreceived = (int)packet[0];
						if ((packet[packet.length-1]==(byte)255) && (packet[packet.length-2]==(byte)255)){
							receivecomplete = true;
						}
					}
					else if((int)packet[0] - lastreceived == 0){//If frame is the same number as last received
						/*
						 * Do something about dupe frame
						 */
						terminal1.println("Dupe frame received");
					}
					else{
						terminal1.println("ERROR - Last packet received: " + lastreceived + " current packet: " + (int)packet[0]);
					}
				}
			}
			terminal1.println("Receive complete");
		}			
	}	

	
	/*
	 * Main method of the Sender thread that starts the sending thread
	 * and the receiving thread.
	 */
	public void run() {
		(new Thread(new receivingThread(),"Receiver's receiving thread")).start();	
		(new Thread(new sendingThread(),"Receiver's sending thread")).start();	
	}
}